/**    
 * 文件名：AutoPayBillService.java</br>
 *    
 * 版本信息：v1.0</br>
 * 日期：2017年11月28日</br>
 * © 2005-2017 雷技信息科技（上海）有限公司版权所有</br>
 *
 */
package com.logic.landseaserver.models.prepayment.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.logic.landseaserver.common.LandeaConstants.BalanceType;
import com.logic.landseaserver.common.LandeaConstants.SendWechatType;
import com.logic.landseaserver.common.LandeaConstants.SmsSendStatus;
import com.logic.landseaserver.common.enums.BillTypeEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.PaymentResultCode;
import com.logic.landseaserver.common.util.Arith;
import com.logic.landseaserver.common.util.StringTools;
import com.logic.landseaserver.domain.Account;
import com.logic.landseaserver.domain.Contract;
import com.logic.landseaserver.domain.SendWechatRecord;
import com.logic.landseaserver.models.prepayment.bean.AccountBalanceResp;
import com.logic.landseaserver.models.prepayment.bean.AotuPayBill;
import com.logic.landseaserver.persistence.read.AccountBalanceReadMapper;
import com.logic.landseaserver.persistence.read.AccountReadMapper;
import com.logic.landseaserver.persistence.read.ContractReadMapper;
import com.logic.landseaserver.persistence.read.SendWechatRecordReadMapper;
import com.logic.landseaserver.persistence.write.SendWechatRecordWriteMapper;
import com.logic.landseaserver.service.impl.SendMsgServiceImpl;
import com.logic.landseaserver.ws.dto.PayRechargeDTO;

/**
 *
 * 项目名称：system-server</br>
 * 类名称：AutoPayBillService</br>
 * 类描述：余额自动抵扣账单</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年11月28日 下午5:16:05</br>
 * 
 * @version 1.0
 *
 */
@Service
public class AutoPayBillService
{
    protected static final Logger LOGGER = LoggerFactory.getLogger(AutoPayBillService.class);
    
    @Autowired
    private AccountReadMapper accountReadMapper;
    
    @Autowired
    private ContractReadMapper contractReadMapper;
    
    @Autowired
    private AccountBalanceReadMapper accountBalanceReadMapper;
    
    @Autowired
    private SendMsgServiceImpl sendMsgServiceImpl;
    
    @Autowired
    private DoAutoPayService doAutoPayService;
    
    @Autowired
    private SendWechatRecordWriteMapper sendWechatRecordWriteMapper;
    
    @Autowired
    private SendWechatRecordReadMapper sendWechatRecordReadMapper;
    
    public void autoPayByContract(Integer contractId)
        throws LandseaException
    {
        LOGGER.info("autoPay|开始处理余额自动抵扣.contractId=" + contractId);
        
        try
        {
            Contract contract = contractReadMapper.selectByPrimaryKey(contractId);
            if (null == contract || contract.getDeleted())
            {
                LOGGER.error("autoPay|合同不存在.contractId=" + contractId);
            }
            
            Account account = accountReadMapper.selectAccount(contract.getRoomId(), contract.getUserId());
            if (null == account || account.getDeleted())
            {
                LOGGER.error("autoPay|账户不存在.userId=" + contract.getUserId() + ",roomId=" + contract.getRoomId());
            }
            
            autoPay(account.getId(), account, true);
        }
        catch (Exception e)
        {
            LOGGER.error("autoPay|余额自动抵扣发生错误.contractId=" + contractId, e);
        }
    }
    
    /**
     * 
     * [简要描述]：自动抵扣</br>
     * [详细描述]：调用者请保证已经提交自己的事务后</br>
     * [作者]：Aaron(2017-12-04)</br>
     *
     * @param userId
     * @param projectId
     * @param roomId
     * @throws LandseaException
     *
     */
    public void autoPayByUserRoom(Integer userId, Integer roomId)
    {
        LOGGER.info("autoPay|开始处理余额自动抵扣.userId=" + userId + ",roomId=" + roomId);
        
        try
        {
            Account account = accountReadMapper.selectAccount(roomId, userId);
            if (null == account || account.getDeleted())
            {
                LOGGER.error("autoPay|账户不存在.userId=" + userId + ",roomId=" + roomId);
            }
            
            // 发送即时消息
            autoPay(account.getId(), account, true);
        }
        catch (Exception e)
        {
            LOGGER.error("autoPay|余额自动抵扣发生错误userId=" + userId + ",roomId=" + roomId, e);
        }
    }
    
    /**
     * 
     * [简要描述]：每月1号出月结账单-自动抵扣</br>
     * [详细描述]：调用者请保证已经提交自己的事务后</br>
     * [作者]：Aaron(2017-12-04)</br>
     *
     * @param userId
     * @param projectId
     * @param roomId
     * @throws LandseaException
     *
     */
    public void autoPay4CreateMonthBill(Integer userId, Integer roomId)
    {
        LOGGER.info("autoPay4CreateMonthBill|开始处理月结账单出账单后余额自动抵扣.userId=" + userId + ",roomId=" + roomId);
        
        try
        {
            Account account = accountReadMapper.selectAccount(roomId, userId);
            if (null == account || account.getDeleted())
            {
                LOGGER.error("autoPay4CreateMonthBill|账户不存在.userId=" + userId + ",roomId=" + roomId);
            }
            
            autoPay(account.getId(), account, false);
        }
        catch (Exception e)
        {
            LOGGER.error("autoPay4CreateMonthBill|余额自动抵扣发生错误userId=" + userId + ",roomId=" + roomId, e);
        }
    }
    
    /**
     * 
     * [简要描述]：自动抵扣,accountId和Account传之一即可</br>
     * [详细描述]：自动抵扣失败，也不会影响其他流程</br>
     * [作者]：Aaron(2017-11-28)</br>
     *
     * @param accountId 账户ID
     * @param Account 账户对象
     * @param sendRealTimeMsg 是否要发送即时消息 true：是 false:否
     * @throws LandseaException
     *
     */
    public void autoPay(Integer accountId, Account account, boolean sendRealTimeMsg)
    {
        try
        {
            LOGGER.info("autoPay|开始处理余额自动抵扣.accountId=" + accountId);
            
            // 如果account为空才查询
            if (null == account)
            {
                // 查询账户表
                account = accountReadMapper.selectByPrimaryKey(accountId);
                if (null == account || account.getDeleted())
                {
                    LOGGER.error("autoPay|账户不存在ID=" + accountId);
                    throw LandseaException.createException(PaymentResultCode.E00140010);
                }
            }
            
            // 查询合同ID
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put("userId", account.getUserId());
            paramMap.put("roomId", account.getRoomId());
            List<Contract> contractList = contractReadMapper.queryContractWithUserRoom(paramMap);
            if (CollectionUtils.isEmpty(contractList))
            {
                LOGGER.info("autoPay|不存在有效合同.accountId=" + accountId);
                return;
            }
            
            List<Integer> contractIdList = new ArrayList<Integer>();
            for (Contract contract : contractList)
            {
                contractIdList.add(contract.getId());
            }
            
            // 首期月结的合同条件
            String firstMonthContractIdCondition =
                StringTools.packageAndInSqlbyListInt(" and lb.contract_id ", contractIdList);
            // 实时的合同条件
            String realContractIdCondition =
                StringTools.packageAndInSqlbyListInt(" and lrb.contract_id ", contractIdList);
            
            // 查询首期、月结、实时账单(未支付、部分支付)
            paramMap.clear();
            paramMap.put("firstMonthContractIdCondition", firstMonthContractIdCondition);
            paramMap.put("realContractIdCondition", realContractIdCondition);
            // 按照金额升序排列
            List<AotuPayBill> autoPaybillList = accountReadMapper.queryAutoPayBills(paramMap);
            
            // 处理授权码减免
            // 检查是否有滞纳金，如果有，将滞纳金放在最后
            Map<String,Double> couponMap = processCouponAndLateFee(autoPaybillList);
            
            if (CollectionUtils.isEmpty(autoPaybillList))
            {
                LOGGER.info("autoPay|没有可抵扣的账单，结束.AccountId=" + accountId);
                return;
            }
            
            
            // 查询余额
            List<AccountBalanceResp> accountBalanceList = accountBalanceReadMapper.selectByAccountId(accountId);
            if (CollectionUtils.isEmpty(accountBalanceList))
            {
                LOGGER.info("autoPay|没有可抵扣的余额，结束.AccountId=" + accountId);
                return;
            }
            
            // key为费项code，value为余额对象
            Map<String, AccountBalanceResp> accountBalanceMap = new HashMap<String, AccountBalanceResp>();
            
            // 抵扣前总的余额,为后续发送金额变动微信消息使用
            double preAutoTotalLeftMoney = 0.00d;
            
            // 处理余额
            for (AccountBalanceResp accountBalance : accountBalanceList)
            {
                // 记录抵扣前总的余额
                preAutoTotalLeftMoney = Arith.add(preAutoTotalLeftMoney, accountBalance.getMoney());
                
                if (BalanceType.COMMON.equals(accountBalance.getBalanceTypeCode()))
                {
                    accountBalance.setBalanceType("通用");
                }
                accountBalanceMap.put(accountBalance.getBalanceTypeCode(), accountBalance);
            }
            
            AccountBalanceResp autoPayBalance = null;
            for (AotuPayBill aotuPayBill : autoPaybillList)
            {
                try
                {
                    // 判断是否为实时费项
                    if (DoAutoPayService.REALBILL.equals(aotuPayBill.getProcessType()))
                    {
                        // 实时费项只能用通用抵扣
                        autoPayBalance = accountBalanceMap.get(BalanceType.COMMON);
                        doAutoPayService.doAutoPay(autoPayBalance, aotuPayBill);
                    }
                    else
                    {
                        // 如果收授权码减免，先减去钱
                        Double couponMoney = couponMap.get(aotuPayBill.getBillType());
                        if (null != couponMoney)
                        {
                            aotuPayBill.setMoney(Arith.add(aotuPayBill.getMoney(), couponMoney.doubleValue()));
                        }
                        
                        // 月结和首期
                        // 先用专项余额抵扣
                        autoPayBalance = accountBalanceMap.get(aotuPayBill.getBillType());
                        doAutoPayService.doAutoPay(autoPayBalance, aotuPayBill);
                        
                        // 然后通用抵扣，是否需要抵扣由内部处理
                        autoPayBalance = accountBalanceMap.get(BalanceType.COMMON);
                        doAutoPayService.doAutoPay(autoPayBalance, aotuPayBill);
                    }
                }
                catch (Exception e)
                {
                    LOGGER.error("autoPay|本次抵扣发生错误，进入下个抵扣处理.", e);
                }
            }
            
            // 抵扣后总余额
            double afterAutoPayTotalMoney = 0.00d;
            for (AccountBalanceResp accountBalance : accountBalanceMap.values())
            {
                // 记录抵扣前总的余额
                afterAutoPayTotalMoney = Arith.add(afterAutoPayTotalMoney, accountBalance.getMoney());
            }
            
            // 处理余额变动微信消息
            processMoneyChangeWechatMsg(preAutoTotalLeftMoney, afterAutoPayTotalMoney, account, sendRealTimeMsg);
            
        }
        catch (LandseaException e)
        {
            // 只记录日期不抛异常
            LOGGER.error("autoPay|自动抵扣发生错误.accountId=" + accountId + "|" + e.getErrorMsg(), e);
        }
        catch (Exception e)
        {
            // 只记录日期不抛异常
            LOGGER.error("autoPay|自动抵扣发生错误.accountId=" + accountId, e);
        }
    }
    
    /**
     * 
     * [简要描述]：处理授权码减免以及滞纳金</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-01-12)</br>
     *
     * @param autoPaybillList
     *
     */
    private Map<String,Double> processCouponAndLateFee(List<AotuPayBill> autoPaybillList)
    {
        Map<String,Double> couponMap = new HashMap<String,Double>();
        if (CollectionUtils.isEmpty(autoPaybillList))
        {
            return couponMap;
        }
        LOGGER.info("processCouponAndLateFee|处理授权码减免，去除授权码金额，将滞纳金放在最后抵扣");
        Iterator<AotuPayBill> iterator = autoPaybillList.iterator();
        AotuPayBill aotuPayBill = null;
        
        List<AotuPayBill> lateFeeList = new ArrayList<AotuPayBill>();
        
        while (iterator.hasNext())
        {
            aotuPayBill = iterator.next();
            if (aotuPayBill.getMoney() < 0)
            {
                couponMap.put(aotuPayBill.getBillType(), aotuPayBill.getMoney());
                iterator.remove();
            }
            
            if (BillTypeEnum.CTBI13.getCode().equals(aotuPayBill.getBillType()))
            {
                lateFeeList.add(aotuPayBill);
                iterator.remove();
            }
        }
        
        // 将滞纳金添加进去放在最后
        for (AotuPayBill lateFeeBill : lateFeeList)
        {
            autoPaybillList.add(lateFeeBill);
        }
        
        return couponMap;
    }
    
    /**
     * 
     * [简要描述]：</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-01-02)</br>
     *
     * @param preAutoTotalLeftMoney 自动抵扣前金额
     * @param afterAutoPayTotalMoney 自动抵扣后金额
     * @param account 账户对象
     * @param sendRealTimeMsg 发送实时消息：true是 false 否
     *
     */
    public void processMoneyChangeWechatMsg(double preAutoTotalLeftMoney, double afterAutoPayTotalMoney,
        Account account, boolean sendRealTimeMsg)
    {
        LOGGER.info("processMoneyChangeWechatMsg|处理金额变动发送微信消息.AccountId=" + account.getId());
        if (preAutoTotalLeftMoney == afterAutoPayTotalMoney)
        {
            LOGGER.info("processMoneyChangeWechatMsg|金额未变动，处理结束.AccountId=" + account.getId());
            return;
        }
        
        // 变动金额
        double changeMoney = Arith.sub(afterAutoPayTotalMoney, preAutoTotalLeftMoney);
        LOGGER.info("processMoneyChangeWechatMsg|本变动金额为" + changeMoney + ".AccountId=" + account.getId());
        
        Date nowDate = new Date();
        
        // 封装微信消息
        PayRechargeDTO payRechargeDTO = new PayRechargeDTO();
        payRechargeDTO.setUserId(account.getUserId());
        payRechargeDTO.setTradeDate(nowDate);
        payRechargeDTO.setTradeAmount(changeMoney);
        payRechargeDTO.setAccountAmount(afterAutoPayTotalMoney);
        payRechargeDTO.setRoomId(account.getRoomId());
        String wechatMsgContent = sendMsgServiceImpl.formatTradeWechatMsg(payRechargeDTO);
        // 是否要实时发送微信消息
        // 目前只有月结出账单自动抵扣是非实时的，由定时任务处理
        if (sendRealTimeMsg)
        {
            // 需要实时发送
            sendMsgServiceImpl.sendTradeWechatMsg(wechatMsgContent);
        }
        else
        {
            // 入库，等待定时任务发送
            SendWechatRecord sendWechatRecord = new SendWechatRecord();
            sendWechatRecord.setWid(StringTools.generateUuid());
            sendWechatRecord.setContent(wechatMsgContent);
            sendWechatRecord.setStatus(SmsSendStatus.NONE_SEND);
            sendWechatRecord.setUserId(account.getUserId());
            sendWechatRecord.setMessageType(SendWechatType.AUTOPAY_CHANGE_MONEY);
            sendWechatRecord.initCommonField();
            sendWechatRecordWriteMapper.insertSelective(sendWechatRecord);
        }
    }
    
    /**
     * 
     * [简要描述]：</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-01-02)</br>
     *
     *
     */
    public void sendDbWechatMsg()
    {
        try
        {
            LOGGER.info("sendDbWechatMsg|开始发送1号出账单后发送自动抵扣余额变动的微信消息.");
            
            // 删除7天前的记录,不管成功与失败
            Calendar currentDate = Calendar.getInstance();
            currentDate.add(Calendar.DATE, -7);
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put("endTime", currentDate.getTime());
            // paramMap.put("status", SmsSendStatus.SEND_SUCCESS);
            sendWechatRecordWriteMapper.clearWechatMsg(paramMap);
            LOGGER.info("sendDbWechatMsg|删除7天前的记录完毕.");
            
            // 查询出未发送成功的进行发送(只查询今天的)
            Calendar queryDate = Calendar.getInstance();
            queryDate.set(Calendar.HOUR_OF_DAY, 0);
            queryDate.set(Calendar.MINUTE, 0);
            queryDate.set(Calendar.SECOND, 0);
            paramMap.clear();
            paramMap.put("startTime", queryDate.getTime());
            paramMap.put("status", SmsSendStatus.SEND_SUCCESS);
            paramMap.put("messageType", SendWechatType.AUTOPAY_CHANGE_MONEY);
            List<SendWechatRecord> needSendList = sendWechatRecordReadMapper.queryNeedSendSmg(paramMap);
            if (CollectionUtils.isEmpty(needSendList))
            {
                LOGGER.info("sendDbWechatMsg|没有待发送的微信消息,等待下一次定时任务.");
                return;
            }
            
            for (SendWechatRecord sendWechatRecord : needSendList)
            {
                boolean isSuccess = sendMsgServiceImpl.sendTradeWechatMsg(sendWechatRecord.getContent());
                if (!isSuccess)
                {
                    LOGGER.error("sendDbWechatMsg|发送自动抵扣金额变动微信消息失败.sendWechatRecordId=" + sendWechatRecord.getId());
                    updateSendWechatRecordStatus(sendWechatRecord.getId(), SmsSendStatus.SEND_FAILED);
                }
                else
                {
                    updateSendWechatRecordStatus(sendWechatRecord.getId(), SmsSendStatus.SEND_SUCCESS);
                }
            }
            LOGGER.info("sendDbWechatMsg|发送1号出账单后发送自动抵扣余额变动的微信消息-结束.");
        }
        catch (Exception e)
        {
            LOGGER.error("sendDbWechatMsg|发送自动抵扣金额变动微信消息错误.", e);
        }
    }
    
    private void updateSendWechatRecordStatus(Integer id, int status)
    {
        SendWechatRecord sendWechatRecord = new SendWechatRecord();
        sendWechatRecord.setId(id);
        sendWechatRecord.setStatus(status);
        sendWechatRecord.editInitCommonField();
        sendWechatRecordWriteMapper.updateByPrimaryKeySelective(sendWechatRecord);
    }
}
